package com.ti.demo.model;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.metadata.*;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.model.MessageAggregator;
import org.springframework.ai.chat.observation.ChatModelObservationContext;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.chat.observation.ChatModelObservationDocumentation;
import org.springframework.ai.chat.observation.DefaultChatModelObservationConvention;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekAssistantMessage;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import org.springframework.ai.deepseek.api.common.DeepSeekConstants;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.model.tool.*;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.support.UsageCalculator;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: com.ti.demo.model
 * @Author Triagen
 * @Date 2025/9/26 14:19
 */
public class DeepSeekChatImageModel implements ChatModel {

    private static final Logger logger = LoggerFactory.getLogger(DeepSeekChatImageModel.class);

    private static final ChatModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultChatModelObservationConvention();

    private static final ToolCallingManager DEFAULT_TOOL_CALLING_MANAGER = ToolCallingManager.builder().build();

    /**
     * The default options used for the chat completion requests.
     */
    private final DeepSeekChatOptions defaultOptions;

    /**
     * The retry template used to retry the DeepSeek API calls.
     */
    public final RetryTemplate retryTemplate;

    /**
     * Low-level access to the DeepSeek API.
     */
    private final DeepSeekApi deepSeekApi;

    /**
     * Observation registry used for instrumentation.
     */
    private final ObservationRegistry observationRegistry;

    /**
     * The tool calling manager used to execute tools.
     */
    private final ToolCallingManager toolCallingManager;

    /**
     * The tool execution eligibility predicate used to determine if a tool can be
     * executed.
     */
    private final ToolExecutionEligibilityPredicate toolExecutionEligibilityPredicate;

    /**
     * Conventions to use for generating observations.
     */
    private ChatModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;

    public DeepSeekChatImageModel(DeepSeekApi deepSeekApi, DeepSeekChatOptions defaultOptions,
                                  ToolCallingManager toolCallingManager, RetryTemplate retryTemplate,
                                  ObservationRegistry observationRegistry) {
        this(deepSeekApi, defaultOptions, toolCallingManager, retryTemplate, observationRegistry,
                new DefaultToolExecutionEligibilityPredicate());
    }

    public DeepSeekChatImageModel(DeepSeekApi deepSeekApi, DeepSeekChatOptions defaultOptions,
                                  ToolCallingManager toolCallingManager, RetryTemplate retryTemplate, ObservationRegistry observationRegistry,
                                  ToolExecutionEligibilityPredicate toolExecutionEligibilityPredicate) {
        Assert.notNull(deepSeekApi, "deepSeekApi cannot be null");
        Assert.notNull(defaultOptions, "defaultOptions cannot be null");
        Assert.notNull(toolCallingManager, "toolCallingManager cannot be null");
        Assert.notNull(retryTemplate, "retryTemplate cannot be null");
        Assert.notNull(observationRegistry, "observationRegistry cannot be null");
        Assert.notNull(toolExecutionEligibilityPredicate, "toolExecutionEligibilityPredicate cannot be null");
        this.deepSeekApi = deepSeekApi;
        this.defaultOptions = defaultOptions;
        this.toolCallingManager = toolCallingManager;
        this.retryTemplate = retryTemplate;
        this.observationRegistry = observationRegistry;
        this.toolExecutionEligibilityPredicate = toolExecutionEligibilityPredicate;
    }

    @Override
    public ChatResponse call(Prompt prompt) {
        Prompt requestPrompt = buildRequestPrompt(prompt);
        return this.internalCall(requestPrompt, null);
    }

    public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatResponse) {

        DeepSeekApi.ChatCompletionRequest request = createRequest(prompt, false);

        ChatModelObservationContext observationContext = ChatModelObservationContext.builder()
                .prompt(prompt)
                .provider(DeepSeekConstants.PROVIDER_NAME)
                .build();

        ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
                .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
                        this.observationRegistry)
                .observe(() -> {

                    ResponseEntity<DeepSeekApi.ChatCompletion> completionEntity = this.retryTemplate
                            .execute(ctx -> this.deepSeekApi.chatCompletionEntity(request));

                    var chatCompletion = completionEntity.getBody();

                    if (chatCompletion == null) {
                        logger.warn("No chat completion returned for prompt: {}", prompt);
                        return new ChatResponse(List.of());
                    }

                    List<DeepSeekApi.ChatCompletion.Choice> choices = chatCompletion.choices();
                    if (choices == null) {
                        logger.warn("No choices returned for prompt: {}", prompt);
                        return new ChatResponse(List.of());
                    }

                    List<Generation> generations = choices.stream().map(choice -> {
                        // @formatter:off
                        Map<String, Object> metadata = Map.of(
                                "id", chatCompletion.id() != null ? chatCompletion.id() : "",
                                "role", choice.message().role() != null ? choice.message().role().name() : "",
                                "index", choice.index(),
                                "finishReason", choice.finishReason() != null ? choice.finishReason().name() : "");
                        // @formatter:on
                        return buildGeneration(choice, metadata);
                    }).toList();

                    // Current usage
                    DeepSeekApi.Usage usage = completionEntity.getBody().usage();
                    Usage currentChatResponseUsage = usage != null ? getDefaultUsage(usage) : new EmptyUsage();
                    Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentChatResponseUsage,
                            previousChatResponse);
                    ChatResponse chatResponse = new ChatResponse(generations,
                            from(completionEntity.getBody(), accumulatedUsage));

                    observationContext.setResponse(chatResponse);

                    return chatResponse;

                });

        if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), response)) {
            var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);
            if (toolExecutionResult.returnDirect()) {
                // Return tool execution result directly to the client.
                return ChatResponse.builder()
                        .from(response)
                        .generations(ToolExecutionResult.buildGenerations(toolExecutionResult))
                        .build();
            } else {
                // Send the tool execution result back to the model.
                return this.internalCall(new Prompt(toolExecutionResult.conversationHistory(), prompt.getOptions()),
                        response);
            }
        }

        return response;
    }

    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        Prompt requestPrompt = buildRequestPrompt(prompt);
        return internalStream(requestPrompt, null);
    }

    public Flux<ChatResponse> internalStream(Prompt prompt, ChatResponse previousChatResponse) {
        return Flux.deferContextual(contextView -> {
            DeepSeekApi.ChatCompletionRequest request = createRequest(prompt, true);

            Flux<DeepSeekApi.ChatCompletionChunk> completionChunks = this.deepSeekApi.chatCompletionStream(request);

            // For chunked responses, only the first chunk contains the choice role.
            // The rest of the chunks with same ID share the same role.
            ConcurrentHashMap<String, String> roleMap = new ConcurrentHashMap<>();

            final ChatModelObservationContext observationContext = ChatModelObservationContext.builder()
                    .prompt(prompt)
                    .provider(DeepSeekConstants.PROVIDER_NAME)
                    .build();

            Observation observation = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION.observation(
                    this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
                    this.observationRegistry);

            observation.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null)).start();

            Flux<ChatResponse> chatResponse = completionChunks.map(this::chunkToChatCompletion)
                    .switchMap(chatCompletion -> Mono.just(chatCompletion).map(chatCompletion2 -> {
                        try {
                            String id = chatCompletion2.id();

                            List<Generation> generations = chatCompletion2.choices().stream().map(choice -> {
                                if (choice.message().role() != null) {
                                    roleMap.putIfAbsent(id, choice.message().role().name());
                                }

                                // @formatter:off
                                Map<String, Object> metadata = Map.of(
                                        "id", chatCompletion2.id(),
                                        "role", roleMap.getOrDefault(id, ""),
                                        "finishReason", choice.finishReason() != null ? choice.finishReason().name() : ""
                                );
                                // @formatter:on
                                return buildGeneration(choice, metadata);
                            }).toList();
                            DeepSeekApi.Usage usage = chatCompletion2.usage();
                            Usage currentUsage = (usage != null) ? getDefaultUsage(usage) : new EmptyUsage();
                            Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse);

                            return new ChatResponse(generations, from(chatCompletion2, cumulativeUsage));
                        } catch (Exception e) {
                            logger.error("Error processing chat completion", e);
                            return new ChatResponse(List.of());
                        }

                    }));

            // @formatter:off
            Flux<ChatResponse> flux = chatResponse.flatMap(response -> {
                        if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), response)) {
                            return Flux.defer(() -> {
                                // FIXME: bounded elastic needs to be used since tool calling
                                //  is currently only synchronous
                                var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);
                                if (toolExecutionResult.returnDirect()) {
                                    // Return tool execution result directly to the client.
                                    return Flux.just(ChatResponse.builder().from(response)
                                            .generations(ToolExecutionResult.buildGenerations(toolExecutionResult))
                                            .build());
                                } else {
                                    // Send the tool execution result back to the model.
                                    return this.internalStream(new Prompt(toolExecutionResult.conversationHistory(), prompt.getOptions()),
                                            response);
                                }
                            }).subscribeOn(Schedulers.boundedElastic());
                        } else {
                            return Flux.just(response);
                        }
                    })
                    .doOnError(observation::error)
                    .doFinally(s -> observation.stop())
                    .contextWrite(ctx -> ctx.put(ObservationThreadLocalAccessor.KEY, observation));
            // @formatter:on

            return new MessageAggregator().aggregate(flux, observationContext::setResponse);

        });
    }

    private Generation buildGeneration(DeepSeekApi.ChatCompletion.Choice choice, Map<String, Object> metadata) {
        List<AssistantMessage.ToolCall> toolCalls = choice.message().toolCalls() == null ? List.of()
                : choice.message()
                .toolCalls()
                .stream()
                .map(toolCall -> new AssistantMessage.ToolCall(toolCall.id(), "function",
                        toolCall.function().name(), toolCall.function().arguments()))
                .toList();

        String finishReason = (choice.finishReason() != null ? choice.finishReason().name() : "");
        var generationMetadataBuilder = ChatGenerationMetadata.builder().finishReason(finishReason);

        String textContent = choice.message().content();
        String reasoningContent = choice.message().reasoningContent();

        DeepSeekAssistantMessage assistantMessage = new DeepSeekAssistantMessage(textContent, reasoningContent,
                metadata, toolCalls);
        return new Generation(assistantMessage, generationMetadataBuilder.build());
    }

    private ChatResponseMetadata from(DeepSeekApi.ChatCompletion result, Usage usage) {
        Assert.notNull(result, "DeepSeek ChatCompletionResult must not be null");
        var builder = ChatResponseMetadata.builder()
                .id(result.id() != null ? result.id() : "")
                .usage(usage)
                .model(result.model() != null ? result.model() : "")
                .keyValue("created", result.created() != null ? result.created() : 0L)
                .keyValue("system-fingerprint", result.systemFingerprint() != null ? result.systemFingerprint() : "");
        return builder.build();
    }

    private ChatResponseMetadata from(ChatResponseMetadata chatResponseMetadata, Usage usage) {
        Assert.notNull(chatResponseMetadata, "DeepSeek ChatResponseMetadata must not be null");
        var builder = ChatResponseMetadata.builder()
                .id(chatResponseMetadata.getId() != null ? chatResponseMetadata.getId() : "")
                .usage(usage)
                .model(chatResponseMetadata.getModel() != null ? chatResponseMetadata.getModel() : "");
        return builder.build();
    }

    /**
     * Convert the ChatCompletionChunk into a ChatCompletion. The Usage is set to null.
     *
     * @param chunk the ChatCompletionChunk to convert
     * @return the ChatCompletion
     */
    private DeepSeekApi.ChatCompletion chunkToChatCompletion(DeepSeekApi.ChatCompletionChunk chunk) {
        List<DeepSeekApi.ChatCompletion.Choice> choices = chunk.choices()
                .stream()
                .map(chunkChoice -> new DeepSeekApi.ChatCompletion.Choice(chunkChoice.finishReason(), chunkChoice.index(), chunkChoice.delta(),
                        chunkChoice.logprobs()))
                .toList();

        return new DeepSeekApi.ChatCompletion(chunk.id(), choices, chunk.created(), chunk.model(), chunk.serviceTier(),
                chunk.systemFingerprint(), chunk.usage());
    }

    private DefaultUsage getDefaultUsage(DeepSeekApi.Usage usage) {
        return new DefaultUsage(usage.promptTokens(), usage.completionTokens(), usage.totalTokens(), usage);
    }

    Prompt buildRequestPrompt(Prompt prompt) {
        DeepSeekChatOptions runtimeOptions = null;
        if (prompt.getOptions() != null) {
            if (prompt.getOptions() instanceof ToolCallingChatOptions toolCallingChatOptions) {
                runtimeOptions = ModelOptionsUtils.copyToTarget(toolCallingChatOptions, ToolCallingChatOptions.class,
                        DeepSeekChatOptions.class);
            } else {
                runtimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(), ChatOptions.class,
                        DeepSeekChatOptions.class);
            }
        }

        DeepSeekChatOptions requestOptions = ModelOptionsUtils.merge(runtimeOptions, this.defaultOptions,
                DeepSeekChatOptions.class);

        if (runtimeOptions != null) {
            requestOptions.setInternalToolExecutionEnabled(
                    ModelOptionsUtils.mergeOption(runtimeOptions.getInternalToolExecutionEnabled(),
                            this.defaultOptions.getInternalToolExecutionEnabled()));
            requestOptions.setToolNames(ToolCallingChatOptions.mergeToolNames(runtimeOptions.getToolNames(),
                    this.defaultOptions.getToolNames()));
            requestOptions.setToolCallbacks(ToolCallingChatOptions.mergeToolCallbacks(runtimeOptions.getToolCallbacks(),
                    this.defaultOptions.getToolCallbacks()));
            requestOptions.setToolContext(ToolCallingChatOptions.mergeToolContext(runtimeOptions.getToolContext(),
                    this.defaultOptions.getToolContext()));
        } else {
            requestOptions.setInternalToolExecutionEnabled(this.defaultOptions.getInternalToolExecutionEnabled());
            requestOptions.setToolNames(this.defaultOptions.getToolNames());
            requestOptions.setToolCallbacks(this.defaultOptions.getToolCallbacks());
            requestOptions.setToolContext(this.defaultOptions.getToolContext());
        }

        ToolCallingChatOptions.validateToolCallbacks(requestOptions.getToolCallbacks());

        return new Prompt(prompt.getInstructions(), requestOptions);
    }

    /**
     * Accessible for testing.
     */
    DeepSeekApi.ChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
        List<DeepSeekApi.ChatCompletionMessage> chatCompletionMessages = prompt.getInstructions().stream().map(message -> {
            if (message.getMessageType() == MessageType.SYSTEM) {
                return List.of(new DeepSeekApi.ChatCompletionMessage(message.getText(),
                        DeepSeekApi.ChatCompletionMessage.Role.valueOf(message.getMessageType().name())));
            } else if (message instanceof UserMessage userMessage) {
                return List.of(new DeepSeekApi.ChatCompletionMessage(getContent(userMessage),
                        DeepSeekApi.ChatCompletionMessage.Role.valueOf(message.getMessageType().name())));
            } else if (message.getMessageType() == MessageType.ASSISTANT) {
                var assistantMessage = (AssistantMessage) message;
                List<DeepSeekApi.ChatCompletionMessage.ToolCall> toolCalls = null;
                if (!CollectionUtils.isEmpty(assistantMessage.getToolCalls())) {
                    toolCalls = assistantMessage.getToolCalls().stream().map(toolCall -> {
                        var function = new DeepSeekApi.ChatCompletionMessage.ChatCompletionFunction(toolCall.name(), toolCall.arguments());
                        return new DeepSeekApi.ChatCompletionMessage.ToolCall(toolCall.id(), toolCall.type(), function);
                    }).toList();
                }
                Boolean isPrefixAssistantMessage = null;
                if (message instanceof DeepSeekAssistantMessage
                        && Boolean.TRUE.equals(((DeepSeekAssistantMessage) message).getPrefix())) {
                    isPrefixAssistantMessage = true;
                }
                return List.of(new DeepSeekApi.ChatCompletionMessage(assistantMessage.getText(),
                        DeepSeekApi.ChatCompletionMessage.Role.ASSISTANT, null, null, toolCalls, isPrefixAssistantMessage, null));
            } else if (message.getMessageType() == MessageType.TOOL) {
                ToolResponseMessage toolMessage = (ToolResponseMessage) message;

                toolMessage.getResponses()
                        .forEach(response -> Assert.isTrue(response.id() != null, "ToolResponseMessage must have an id"));
                return toolMessage.getResponses()
                        .stream()
                        .map(tr -> new DeepSeekApi.ChatCompletionMessage(tr.responseData(), DeepSeekApi.ChatCompletionMessage.Role.TOOL, tr.name(),
                                tr.id(), null))
                        .toList();
            } else {
                throw new IllegalArgumentException("Unsupported message type: " + message.getMessageType());
            }
        }).flatMap(List::stream).toList();

        DeepSeekApi.ChatCompletionRequest request = new DeepSeekApi.ChatCompletionRequest(chatCompletionMessages, stream);

        DeepSeekChatOptions requestOptions = (DeepSeekChatOptions) prompt.getOptions();
        request = ModelOptionsUtils.merge(requestOptions, request, DeepSeekApi.ChatCompletionRequest.class);

        // Add the tool definitions to the request's tools parameter.
        List<ToolDefinition> toolDefinitions = this.toolCallingManager.resolveToolDefinitions(requestOptions);
        if (!CollectionUtils.isEmpty(toolDefinitions)) {
            request = ModelOptionsUtils.merge(
                    DeepSeekChatOptions.builder().tools(this.getFunctionTools(toolDefinitions)).build(), request,
                    DeepSeekApi.ChatCompletionRequest.class);
        }

        return request;
    }

    private JSONArray getContent(UserMessage userMessage) {
        JSONArray jsonArray = new JSONArray();

        JSONObject textObject = new JSONObject();
        textObject.put("type", "text");
        textObject.put("text", userMessage.getText());
        jsonArray.add(textObject);

        userMessage.getMedia().forEach(media -> {
            JSONObject mediaObject = new JSONObject();
            mediaObject.put("type", "image_url");
            JSONObject urlObject = new JSONObject();
            urlObject.put("url", fromMediaData(media.getData()));
            mediaObject.put("image_url", urlObject);
            jsonArray.add(mediaObject);
        });

        return jsonArray;
    }

    private String fromMediaData(Object mediaData) {
        if (mediaData instanceof byte[] bytes) {
            return "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(bytes);
        } else if (mediaData instanceof String text) {
            return text;
        } else {
            throw new IllegalArgumentException("Unsupported media data type: " + mediaData.getClass().getSimpleName());
        }

    }

    private List<DeepSeekApi.FunctionTool> getFunctionTools(List<ToolDefinition> toolDefinitions) {
        return toolDefinitions.stream().map(toolDefinition -> {
            var function = new DeepSeekApi.FunctionTool.Function(toolDefinition.description(), toolDefinition.name(),
                    toolDefinition.inputSchema());
            return new DeepSeekApi.FunctionTool(function);
        }).toList();
    }

    private ChatOptions buildRequestOptions(DeepSeekApi.ChatCompletionRequest request) {
        return ChatOptions.builder()
                .model(request.model())
                .frequencyPenalty(request.frequencyPenalty())
                .maxTokens(request.maxTokens())
                .presencePenalty(request.presencePenalty())
                .stopSequences(request.stop())
                .temperature(request.temperature())
                .topP(request.topP())
                .build();
    }

    @Override
    public ChatOptions getDefaultOptions() {
        return DeepSeekChatOptions.fromOptions(this.defaultOptions);
    }

    @Override
    public String toString() {
        return "DeepSeekChatModel [defaultOptions=" + this.defaultOptions + "]";
    }

    /**
     * Use the provided convention for reporting observation data
     *
     * @param observationConvention The provided convention
     */
    public void setObservationConvention(ChatModelObservationConvention observationConvention) {
        Assert.notNull(observationConvention, "observationConvention cannot be null");
        this.observationConvention = observationConvention;
    }

    public static DeepSeekChatImageModel.Builder builder() {
        return new DeepSeekChatImageModel.Builder();
    }

    public static final class Builder {

        private DeepSeekApi deepSeekApi;

        private DeepSeekChatOptions defaultOptions = DeepSeekChatOptions.builder()
                .model(DeepSeekApi.DEFAULT_CHAT_MODEL)
                .temperature(0.7)
                .build();

        private ToolCallingManager toolCallingManager;

        private ToolExecutionEligibilityPredicate toolExecutionEligibilityPredicate = new DefaultToolExecutionEligibilityPredicate();

        private RetryTemplate retryTemplate = RetryUtils.DEFAULT_RETRY_TEMPLATE;

        private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;

        private Builder() {
        }

        public DeepSeekChatImageModel.Builder deepSeekApi(DeepSeekApi deepSeekApi) {
            this.deepSeekApi = deepSeekApi;
            return this;
        }

        public DeepSeekChatImageModel.Builder defaultOptions(DeepSeekChatOptions defaultOptions) {
            this.defaultOptions = defaultOptions;
            return this;
        }

        public DeepSeekChatImageModel.Builder toolCallingManager(ToolCallingManager toolCallingManager) {
            this.toolCallingManager = toolCallingManager;
            return this;
        }

        public DeepSeekChatImageModel.Builder toolExecutionEligibilityPredicate(
                ToolExecutionEligibilityPredicate toolExecutionEligibilityPredicate) {
            this.toolExecutionEligibilityPredicate = toolExecutionEligibilityPredicate;
            return this;
        }

        public DeepSeekChatImageModel.Builder retryTemplate(RetryTemplate retryTemplate) {
            this.retryTemplate = retryTemplate;
            return this;
        }

        public DeepSeekChatImageModel.Builder observationRegistry(ObservationRegistry observationRegistry) {
            this.observationRegistry = observationRegistry;
            return this;
        }

        public DeepSeekChatImageModel build() {
            if (this.toolCallingManager != null) {
                return new DeepSeekChatImageModel(this.deepSeekApi, this.defaultOptions, this.toolCallingManager,
                        this.retryTemplate, this.observationRegistry, this.toolExecutionEligibilityPredicate);
            }
            return new DeepSeekChatImageModel(this.deepSeekApi, this.defaultOptions, DEFAULT_TOOL_CALLING_MANAGER,
                    this.retryTemplate, this.observationRegistry, this.toolExecutionEligibilityPredicate);
        }

    }

}
